/*
 * @Author: xiaosihan
 * @Date: 2021-04-24 20:23:31
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2021-11-07 19:11:04
 */

import _ from "lodash";
import * as THREE from "three";
import Object3D from "../Object3D/Object3D";
import Object3DComponent from "../Object3D/Object3DComponent";
import { Object3DComponentProps } from "../Object3D/Object3DComponent";
import { Object3DEvent } from "../Renderer/type";
import Transition from "../xrt_util/Transition";

export type LinePoints = Array<[number, number, number]>

export interface LineProps extends Object3DComponentProps {

    points?: LinePoints,
    color?: string[], // 线条的颜色
    opacity?: number,// 透明度
    dashSize?: number, // 虚线长度
    gapSize?: number, // 虚线间隔
    dashOffset?: number, // 虚线位移调整
    dashSpeed?: number, // 虚线流动速度
    fog?: boolean,
}

interface State { }

export default class Line<P> extends Object3DComponent<P & LineProps, State> {
    constructor(props: P & LineProps) {
        super(props);


        let name = this.getConstructorName();

        this.object3d = new (Function("THREELine", `
            return class ${name} extends THREELine {
            };
        `)(THREE.Line));

    }

    declare object3d: THREE.Line & Object3D;

    // 线条模型
    lineGeometry = (() => {
        let lineGeometry = new THREE.BufferGeometry();
        lineGeometry.drawRange.count = 1;
        // 初始顶点位置
        lineGeometry.setAttribute("position", new THREE.Float32BufferAttribute([0, 0, 0], 3));
        lineGeometry.setAttribute("color", new THREE.Float32BufferAttribute([0, 0, 0], 3));
        return lineGeometry;
    })();

    // 线条材质
    lineMaterial = new THREE.LineDashedMaterial({});

    // 带有缓动效果的线顶点对象
    TLineVector3 = new Transition();

    // 时钟对象
    Clock = new THREE.Clock();

    Tcolor = new Transition();

    // 线条的缓动参数
    TLineParam = new Transition();

    componentDidMount() {
        this.object3d.geometry.dispose();
        (this.object3d.material as any).dispose();
        this.object3d.geometry = this.lineGeometry;
        this.object3d.material = this.lineMaterial;

        this.setLinGeometry();
        this.setTansformColor();
        this.setVectorColor();
        this.setLineMaterial();

        // 设置顶点位置
        this.object3d.addEventListener(Object3DEvent.BEFORE_RENDER, this.setTransformVector3Position);
    }

    componentDidUpdate() {
        this.setLinGeometry();
        this.setTansformColor();
        this.setLineMaterial();

        this.object3d.removeEventListener(Object3DEvent.BEFORE_RENDER, this.setTransformVector3Position);
        this.object3d.addEventListener(Object3DEvent.BEFORE_RENDER, this.setTransformVector3Position);

    }

    componentWillUnmount() {
        this.object3d.geometry.dispose();
        (this.object3d.material as any).dispose();
        this.object3d.removeEventListener(Object3DEvent.BEFORE_RENDER, this.setTransformVector3Position);
    }

    // 设置线模型
    setLinGeometry() {

        const { bezier, duration = 500, points = [] } = this.props;

        const { lineGeometry } = this;

        let newPoints = _.flatten(points);// 把传入的参数转成顶点格式 一维数组

        this.TLineVector3.setBezier(bezier).setDuration(duration).set(newPoints);

        // 长度不相等时 重新创建 线条position顶点对象
        if (lineGeometry.attributes.position.array.length < newPoints.length) {

            let originPoints = lineGeometry.attributes.position.array;

            let newPosition = new THREE.Float32BufferAttribute(newPoints, 3)

            newPosition.set(originPoints);

            lineGeometry.setAttribute("position", newPosition);
        }

        lineGeometry.drawRange.count = points.length;
    }

    static ColorTemp = new THREE.Color();

    // 设置顶点过渡颜色对象
    setVectorColor() {
        const { lineGeometry } = this;

        if (!this.Tcolor.isDone()) {

            let colorArray = Object.values(this.Tcolor.getJson()) as Array<number>;

            let lineColor = lineGeometry.attributes.color;

            // 设置线条顶点的颜色
            for (let i = 0; i < lineColor.array.length; i += colorArray.length) {

                if (lineColor.array.length - colorArray.length >= i) {
                    (lineColor.array as Float32Array).set(colorArray, i);
                } else {
                    (lineColor.array as Float32Array).set(colorArray.slice(0, lineColor.array.length % colorArray.length), i);
                }

            }

            lineColor.needsUpdate = true; // 标记顶点位置需要更新

            return true;

        } else {
            return false

        }
    }

    setTansformColor() {
        let { bezier, duration = 500, color = ["#fff"] } = this.props;

        const { lineGeometry } = this;

        let newVectorColor: number[] = [];

        (_.isEmpty(color) ? ["#fff"] : color).map(c => {
            const { r, g, b } = Line.ColorTemp.set(c);
            newVectorColor.push(r, g, b);
        });


        // 长度不相等时 重新创建 线条position顶点对象
        if (lineGeometry.attributes.color.array.length < lineGeometry.attributes.position.array.length) {

            let newColor = new THREE.Float32BufferAttribute(lineGeometry.attributes.position.array.length, 3);
            let originColor = lineGeometry.attributes.color.array;

            newColor.set(originColor);

            lineGeometry.setAttribute("color", newColor);
        }

        this.Tcolor.setBezier(bezier).setDuration(duration).set(newVectorColor);
    }

    // 设置线材质
    setLineMaterial() {

        const {
            bezier,
            duration = 500,
            opacity = 1,
            dashSize = 1,
            gapSize = 0,
            dashOffset = 0,
            fog = false
        } = this.props;

        this.TLineParam
            .setBezier(bezier)
            .setDuration(duration)
            .set({ opacity, dashSize, gapSize });

        Object.assign(this.lineMaterial, {
            dashOffset,
            vertexColors: true,
            transparent: true,
            fog
        });

        this.lineMaterial.needsUpdate = true;
    }

    // 虚线流动计算
    componentBeforeRender(renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.Camera) {
        const { lineGeometry } = this;
        const { dashSpeed = 0 } = this.props;

        // 虚线流动计算
        if (dashSpeed !== 0 && lineGeometry.attributes.lineDistance) {
            let distanceArray = lineGeometry.attributes.lineDistance.array;

            let increment = dashSpeed * this.Clock.getDelta();

            for (let i in distanceArray) {
                //@ts-ignore
                distanceArray.set([distanceArray[i] - increment], i);
            }

            lineGeometry.attributes.lineDistance.needsUpdate = true; // 标记虚线距离需要更新
        }

    }

    // 设置顶点的位置
    setTransformVector3Position = () => {

        const { lineGeometry, lineMaterial } = this;

        let keepRun = false;

        // 设置每个顶点的位置
        if (!this.TLineVector3.isDone()) {
            for (let i = 0; i < lineGeometry.drawRange.count; i++) {
                lineGeometry.attributes.position.setXYZ(
                    i,
                    this.TLineVector3.get(i * 3),
                    this.TLineVector3.get((i * 3) + 1),
                    this.TLineVector3.get((i * 3) + 2)
                );
            }
            lineGeometry.attributes.position.needsUpdate = true; // 标记顶点位置需要更新
            keepRun = true;
        }

        // 设置材质上面的参数
        if (!this.TLineParam.isDone()) {
            const { opacity, dashSize, gapSize } = this.TLineParam.getJson();
            Object.assign(lineMaterial, {
                opacity,
                dashSize,
                gapSize
            });

            this.object3d.computeLineDistances(); // 虚线计算

            keepRun = true;
        }

        if (this.setVectorColor()) {
            keepRun = true;
        }

        if (!keepRun) {
            this.object3d.removeEventListener(Object3DEvent.BEFORE_RENDER, this.setTransformVector3Position);
        }
    }

}